<?php
//This is a source code or part of Huggle project
//
//This file contains code for requests
//last modified by Addshore

//Copyright (C) 2011-2012 Huggle team

//This program is free software: you can redistribute it and/or modify
//it under the terms of the GNU General Public License as published by
//the Free Software Foundation, either version 3 of the License, or
//(at your option) any later version.

//This program is distributed in the hope that it will be useful,
//but WITHOUT ANY WARRANTY; without even the implied warranty of
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//GNU General Public License for more details.

/*
 *  Developers of the origional code that these functions were based upon:
 *      Cobi    - [[User:Cobi]]     - Wrote the http class and some of the wikipedia class
 *      Chris   - [[User:Chris_G]]  - Wrote the most of the wikipedia class
 *      Fale    - [[User:Fale]]     - Polish, wrote the extended and some of the wikipedia class
 *      Gutza   - [[User:Gutza]]    - Submitted a patch for http->setHTTPcreds()
 *      Addshore- [[User:Addshore]] - Altered all code for use in huggle-wa
 **/

class wikipedia {
    private $http;
    private $token;
    private $ecTimestamp;
    public $url;

    function __construct ($url='http://en.wikipedia.org/w/api.php',$hu=null,$hp=null) {
        $this->http = new http;
        $this->token = null;
        $this->url = $url;
        $this->ecTimestamp = null;
        if ($hu!==null)
            $this->http->setHTTPcreds($hu,$hp);
    }
	
	/**
     * Sends a query to the api.
     * @param $query The query string.
     * @param $post POST data if its a post request (optional).
     * @return The api result.
     **/
    function query ($query,$post=null) {
        if ($post==null)
            $ret = $this->http->get($this->url.$query);
        else
            $ret = $this->http->post($this->url.$query,$post);
        return unserialize($ret);
    }

    /**
     * Gets the content of a page. Returns false on error.
     * @param $page The wikipedia page to fetch.
     * @param $revid The revision id to fetch (optional)
     * @return The wikitext for the page.
     **/
    function getpage ($page,$revid=null,$detectEditConflict=false) {
        $append = '';
        if ($revid!=null)
            $append = '&rvstartid='.$revid;
        $x = $this->query('?action=query&format=php&prop=revisions&titles='.urlencode($page).'&rvlimit=1&rvprop=content|timestamp'.$append);
        foreach ($x['query']['pages'] as $ret) {
            if (isset($ret['revisions'][0]['*'])) {
                if ($detectEditConflict)
                    $this->ecTimestamp = $ret['revisions'][0]['timestamp'];
                return $ret['revisions'][0]['*'];
            } else
                return false;
        }
    }
	
	/**
     * Gets the page id for a page.
     * @param $page The wikipedia page to get the id for.
     * @return The page id of the page.
     **/
    function getpageid ($page) {
        $x = $this->query('?action=query&format=php&prop=revisions&titles='.urlencode($page).'&rvlimit=1&rvprop=content');
        foreach ($x['query']['pages'] as $ret) {
            return $ret['pageid'];
        }
    }

    /**
     * Gets the number of contributions a user has.
     * @param $user The username for which to get the edit count.
     * @return The number of contributions the user has.
     **/
    function contribcount ($user) {
        $x = $this->query('?action=query&list=allusers&format=php&auprop=editcount&aulimit=1&aufrom='.urlencode($user));
        return $x['query']['allusers'][0]['editcount'];
    }

    /**
     * Returns an array with all the members of $category
     * @param $category The category to use.
     * @param $subcat (bool) Go into sub categories?
     * @return array
     **/
    function categorymembers ($category,$subcat=false) {
        $continue = '';
        $pages = array();
        while (true) {
            $res = $this->query('?action=query&list=categorymembers&cmtitle='.urlencode($category).'&format=php&cmlimit=500'.$continue);
            if (isset($x['error'])) {
                return false;
            }
            foreach ($res['query']['categorymembers'] as $x) {
                $pages[] = $x['title'];
            }
            if (empty($res['query-continue']['categorymembers']['cmcontinue'])) {
                if ($subcat) {
                    foreach ($pages as $p) {
                        if (substr($p,0,9)=='Category:') {
                            $pages2 = $this->categorymembers($p,true);
                            $pages = array_merge($pages,$pages2);
                        }
                    }
                }
                return $pages;
            } else {
                $continue = '&cmcontinue='.urlencode($res['query-continue']['categorymembers']['cmcontinue']);
            }
        }
    }

    /**
     * Returns a list of pages that link to $page.
     * @param $page
     * @param $extra (defaults to null)
     * @return array
     **/
    function whatlinkshere ($page,$extra=null) {
        $continue = '';
        $pages = array();
        while (true) {
            $res = $this->query('?action=query&list=backlinks&bltitle='.urlencode($page).'&bllimit=500&format=php'.$continue.$extra);
            if (isset($res['error'])) {
                return false;
            }
            foreach ($res['query']['backlinks'] as $x) {
                $pages[] = $x['title'];
            }
            if (empty($res['query-continue']['backlinks']['blcontinue'])) {
                return $pages;
            } else {
                $continue = '&blcontinue='.urlencode($res['query-continue']['backlinks']['blcontinue']);
            }
        }
    }

    /**
    * Returns a list of pages that include the image.
    * @param $image
    * @param $extra (defaults to null)
    * @return array
    **/
    function whereisincluded ($image,$extre=null) {
        $continue = '';
        $pages = array();
        while (true) {
            $res = $this->query('?action=query&list=imageusage&iutitle='.urlencode($image).'&iulimit=500&format=php'.$continue.$extra);
            if (isset($res['error']))
                return false;
            foreach ($res['query']['imageusage'] as $x) {
                $pages[] = $x['title'];
            }
            if (empty($res['query-continue']['imageusage']['iucontinue']))
                return $pages;
            else
                $continue = '&iucontinue='.urlencode($res['query-continue']['imageusage']['iucontinue']);
        }
    }
    
    /**
    * Returns a list of pages that use the $template.
    * @param $template the template we are intereste into
    * @param $extra (defaults to null)
    * @return array
    **/
    function whatusethetemplate ($template,$extra=null) {
        $continue = '';
        $pages = array();
        while (true) {
            $res = $this->query('?action=query&list=embeddedin&eititle=Template:'.urlencode($template).'&eilimit=500&format=php'.$continue.$extra);
            if (isset($res['error'])) {
                return false;
            }
            foreach ($res['query']['embeddedin'] as $x) {
                $pages[] = $x['title'];
            }
            if (empty($res['query-continue']['embeddedin']['eicontinue'])) {
                return $pages;
            } else {
                $continue = '&eicontinue='.urlencode($res['query-continue']['embeddedin']['eicontinue']);
            }
         }
     }
    
    /**
     * Returns an array with all the subpages of $page
     * @param $page
     * @return array
     **/
    function subpages ($page) {
        /* Calculate all the namespace codes */
        $ret = $this->query('?action=query&meta=siteinfo&siprop=namespaces&format=php');
        foreach ($ret['query']['namespaces'] as $x) {
            $namespaces[$x['*']] = $x['id'];
        }
        $temp = explode(':',$page,2);
        $namespace = $namespaces[$temp[0]];
        $title = $temp[1];
        $continue = '';
        $subpages = array();
        while (true) {
            $res = $this->query('?action=query&format=php&list=allpages&apprefix='.urlencode($title).'&aplimit=500&apnamespace='.$namespace.$continue);
            if (isset($x[error])) {
                return false;
            }
            foreach ($res['query']['allpages'] as $p) {
                $subpages[] = $p['title'];
            }
            if (empty($res['query-continue']['allpages']['apfrom'])) {
                return $subpages;
            } else {
                $continue = '&apfrom='.urlencode($res['query-continue']['allpages']['apfrom']);
            }
        }
    }

    /**
     * This function takes a username and password and logs you into wikipedia.
     * @param $user Username to login as.
     * @param $pass Password that corrisponds to the username.
     * @return array
     **/
    function login ($user,$pass) {
        $post = array('lgname' => $user, 'lgpassword' => $pass);
        $ret = $this->query('?action=login&format=php',$post);
        /* This is now required - see https://bugzilla.wikimedia.org/show_bug.cgi?id=23076 */
        if ($ret['login']['result'] == 'NeedToken') {
            $post['lgtoken'] = $ret['login']['token'];
            $ret = $this->query( '?action=login&format=php', $post );
        }
		return $ret;
		//May want to return $ret['login']['result'];
    }

    /**
     * This function returns the edit token for the current user.
     * @return edit token.
     **/
    function getedittoken () {
        $x = $this->query('?action=query&prop=info&intoken=edit&titles=Main%20Page&format=php');
        foreach ($x['query']['pages'] as $ret) {
            return $ret['edittoken'];
        }
    }

    /**
     * Purges the cache of $page.
     * @param $page The page to purge.
     * @return Api result.
     **/
    function purgeCache($page) {
        return $this->query('?action=purge&titles='.urlencode($page).'&format=php');
    }

    /**
     * Checks if $user has email enabled.
     * Uses index.php.
     * @param $user The user to check.
     * @return bool.
     **/
    function checkEmail($user) {
        $x = $this->query('?action=query&meta=allmessages&ammessages=noemailtext|notargettext&amlang=en&format=php');
        $messages[0] = $x['query']['allmessages'][0]['*'];
        $messages[1] = $x['query']['allmessages'][1]['*'];
        $page = $this->http->get(str_replace('api.php','index.php',$this->url).'?title=Special:EmailUser&target='.urlencode($user));
        if (preg_match('/('.preg_quote($messages[0],'/').'|'.preg_quote($messages[1],'/').')/i',$page)) {
            return false;
        } else {
            return true;
        }
    }

    /**
     * Returns all the pages $page is transcluded on.
     * @param $page The page to get the transclusions from.
     * @param $sleep The time to sleep between requets (set to null to disable).
     * @return array.
     **/
    function getTransclusions($page,$sleep=null,$extra=null) {
        $continue = '';
        $pages = array();
        while (true) {
            $ret = $this->query('?action=query&list=embeddedin&eititle='.urlencode($page).$continue.$extra.'&eilimit=500&format=php');
            if ($sleep != null) {
                sleep($sleep);
            }
            foreach ($ret['query']['embeddedin'] as $x) {
                $pages[] = $x['title'];
            }
            if (isset($ret['query-continue']['embeddedin']['eicontinue'])) {
                $continue = '&eicontinue='.$ret['query-continue']['embeddedin']['eicontinue'];
            } else {
                return $pages;
            }
        }
    }

    /**
     * Edits a page.
     * @param $page Page name to edit.
     * @param $data Data to post to page.
     * @param $summary Edit summary to use.
     * @param $minor Whether or not to mark edit as minor.  (Default false)
     * @param $bot Whether or not to mark edit as a bot edit.  (Default true)
     * @return api result
     **/
    function edit ($page,$data,$summary = '',$minor = false,$bot = true,$section = null,$detectEC=false,$maxlag='') {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'title' => $page,
            'text' => $data,
            'token' => $this->token,
            'summary' => $summary,
            ($minor?'minor':'notminor') => '1',
            ($bot?'bot':'notbot') => '1'
        );
        if ($section != null) {
            $params['section'] = $section;
        }
        if ($this->ecTimestamp != null && $detectEC == true) {
            $params['basetimestamp'] = $this->ecTimestamp;
            $this->ecTimestamp = null;
        }
        if ($maxlag!='') {
            $maxlag='&maxlag='.$maxlag;
        }
        return $this->query('?action=edit&format=php'.$maxlag,$params);
    }

    /**
    * Add a text at the bottom of a page
    * @param $page The page we're working with.
    * @param $text The text that you want to add.
    * @param $summary Edit summary to use.
    * @param $minor Whether or not to mark edit as minor.  (Default false)
    * @param $bot Whether or not to mark edit as a bot edit.  (Default true)
    * @return api result
    **/
    function addtext( $page, $text, $summary = '', $minor = false, $bot = true )
    {
        $data = $this->getpage( $page );
        $data.= "\n" . $text;
        return $this->edit( $page, $data, $summary, $minor, $bot );
    }
    
    /**
     * Moves a page.
     * @param $old Name of page to move.
     * @param $new New page title.
     * @param $reason Move summary to use.
     * @param $movetalk Move the page's talkpage as well.
     * @return api result
     **/
    function move ($old,$new,$reason,$options=null) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'from' => $old,
            'to' => $new,
            'token' => $this->token,
            'reason' => $reason
        );
        if ($options != null) {
            $option = explode('|',$options);
            foreach ($option as $o) {
                $params[$o] = true;
            }
        }
        return $this->query('?action=move&format=php',$params);
    }

    /**
     * Rollback an edit.
     * @param $title Title of page to rollback.
     * @param $user Username of last edit to the page to rollback.
     * @param $reason Edit summary to use for rollback.
     * @param $bot mark the rollback as bot.
     * @return api result
     **/
    function rollback ($title,$user,$reason=null,$bot=false) {
        $ret = $this->query('?action=query&prop=revisions&rvtoken=rollback&titles='.urlencode($title).'&format=php');
        foreach ($ret['query']['pages'] as $x) {
            $token = $x['revisions'][0]['rollbacktoken'];
            break;
        }
        $params = array(
            'title' => $title,
            'user' => $user,
            'token' => $token
        );
        if ($bot) {
            $params['markbot'] = true;
        }
        if ($reason != null) { $params['summary'] = $reason; }
            return $this->query('?action=rollback&format=php',$params);
        }

    /**
     * Blocks a user.
     * @param $user The user to block.
     * @param $reason The block reason.
     * @param $expiry The block expiry.
     * @param $options a piped string containing the block options.
     * @return api result
     **/
    function block ($user,$reason='vand',$expiry='infinite',$options=null,$retry=true) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'expiry' => $expiry,
            'user' => $user,
            'reason' => $reason,
            'token' => $this->token
        );
        if ($options != null) {
            $option = explode('|',$options);
            foreach ($option as $o) {
                $params[$o] = true;
            }
        }
        $ret = $this->query('?action=block&format=php',$params);
        /* Retry on a failed token. */
        if ($retry and $ret['error']['code']=='badtoken') {
            $this->token = $this->getedittoken();
            return $this->block($user,$reason,$expiry,$options,false);
        }
        return $ret;
    }

    /**
     * Unblocks a user.
     * @param $user The user to unblock.
     * @param $reason The unblock reason.
     * @return api result
     **/
    function unblock ($user,$reason) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'user' => $user,
            'reason' => $reason,
            'token' => $this->token
        );
        return $this->query('?action=unblock&format=php',$params);
    }

    /**
     * Emails a user.
     * @param $target The user to email.
     * @param $subject The email subject.
     * @param $text The body of the email.
     * @param $ccme Send a copy of the email to the user logged in.
     * @return api result
     **/
    function email ($target,$subject,$text,$ccme=false) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'target' => $target,
            'subject' => $subject,
            'text' => $text,
            'token' => $this->token
        );
        if ($ccme) {
            $params['ccme'] = true;
        }
        return $this->query('?action=emailuser&format=php',$params);
    }

    /**
     * Deletes a page.
     * @param $title The page to delete.
     * @param $reason The delete reason.
     * @return api result
     **/
    function delete ($title,$reason) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'title' => $title,
            'reason' => $reason,
            'token' => $this->token
        );
        return $this->query('?action=delete&format=php',$params);
    }

    /**
     * Undeletes a page.
     * @param $title The page to undelete.
     * @param $reason The undelete reason.
     * @return api result
     **/
    function undelete ($title,$reason) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'title' => $title,
            'reason' => $reason,
            'token' => $this->token
        );
        return $this->query('?action=undelete&format=php',$params);
    }

    /**
     * (Un)Protects a page.
     * @param $title The page to (un)protect.
     * @param $protections The protection levels (e.g. 'edit=autoconfirmed|move=sysop')
     * @param $expiry When the protection should expire (e.g. '1 day|infinite')
     * @param $reason The (un)protect reason.
     * @param $cascade Enable cascading protection? (defaults to false)
     * @return api result
     **/
    function protect ($title,$protections,$expiry,$reason,$cascade=false) {
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        $params = array(
            'title' => $title,
            'protections' => $protections,
            'expiry' => $expiry,
            'reason' => $reason,
            'token' => $this->token
        );
        if ($cascade) {
            $params['cascade'] = true;
        }
        return $this->query('?action=protect&format=php',$params);
    }
    
    /*
    $page - page
    $revs - rev ids to delete (seperated with ,)
    $comment - delete comment
    */
    function revdel ($page,$revs,$comment) {
        
        if ($this->token==null) {
            $this->token = $this->getedittoken();
        }
        
        $post = array(
            'wpEditToken'       => $this->token,
            'ids' => $revs,
            'target' => $page,
            'type' => 'revision',
            'wpHidePrimary' => 1,
            'wpHideComment' => 1,
            'wpHideUser' => 0,
            'wpRevDeleteReasonList' => 'other',
            'wpReason' => $comment,
            'wpSubmit' => 'Apply to selected revision(s)'
        );
        return $this->http->post(str_replace('api.php','index.php',$this->url).'?title=Special:RevisionDelete&action=submit',$post);
    }

    /**
     * Changes a users rights.
     * @param $user   The user we're working with.
     * @param $add    A pipe-separated list of groups you want to add.
     * @param $remove A pipe-separated list of groups you want to remove.
     * @param $reason The reason for the change (defaults to '').
     **/
    function userrights ($user,$add,$remove,$reason='') {
        // get the userrights token
        $token = $this->query('?action=query&list=users&ususers='.urlencode($user).'&ustoken=userrights&format=php');
        $token = $token['query']['users'][0]['userrightstoken'];
        $params = array(
            'user' => $user,
            'token' => $token,
            'add' => $add,
            'remove' => $remove,
            'reason' => $reason
        );
        return $this->query('?action=userrights&format=php',$params);
    }

	
}//end wikipedia class

class http {
    private $ch;
    private $uid;
    public $cookie_jar;

    function data_encode ($data, $keyprefix = "", $keypostfix = "") {
        assert( is_array($data) );
        $vars=null;
        foreach($data as $key=>$value) {
            if(is_array($value))
                $vars .= $this->data_encode($value, $keyprefix.$key.$keypostfix.urlencode("["), urlencode("]"));
            else
                $vars .= $keyprefix.$key.$keypostfix."=".urlencode($value)."&";
        }
        return $vars;
    }

    function __construct () {
        $this->ch = curl_init();
		//Generate a random number for the cookie, if it exists do it again
		do {
			$this->uid = dechex(rand(0,99999999));
		}while(file_exists('/tmp/huggle.cookies.'.$this->uid.'.dat') == true)
        curl_setopt($this->ch,CURLOPT_COOKIEJAR,'/tmp/huggle.cookies.'.$this->uid.'.dat');
        curl_setopt($this->ch,CURLOPT_COOKIEFILE,'/tmp/huggle.cookies.'.$this->uid.'.dat');
        curl_setopt($this->ch,CURLOPT_MAXCONNECTS,100);
        curl_setopt($this->ch,CURLOPT_CLOSEPOLICY,CURLCLOSEPOLICY_LEAST_RECENTLY_USED);
        $this->cookie_jar = array();
    }

    function post ($url,$data) {
        $time = microtime(1);
        curl_setopt($this->ch,CURLOPT_URL,$url);
        curl_setopt($this->ch,CURLOPT_USERAGENT,'huggle_wa');
        $cookies = null;
        foreach ($this->cookie_jar as $name => $value) {
            if (empty($cookies))
                $cookies = "$name=$value";
            else
                $cookies .= "; $name=$value";
        }
        if ($cookies != null)
            curl_setopt($this->ch,CURLOPT_COOKIE,$cookies);
        curl_setopt($this->ch,CURLOPT_MAXREDIRS,10);
        curl_setopt($this->ch, CURLOPT_HTTPHEADER, array('Expect:'));
        curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($this->ch,CURLOPT_TIMEOUT,30);
        curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10);
        curl_setopt($this->ch,CURLOPT_POST,1);
        curl_setopt($this->ch,CURLOPT_POSTFIELDS, $data);
        $data = curl_exec($this->ch);
        return $data;
    }

    function get ($url) {
        $time = microtime(1);
        curl_setopt($this->ch,CURLOPT_URL,$url);
        curl_setopt($this->ch,CURLOPT_USERAGENT,'huggle_wa');
        $cookies = null;
        foreach ($this->cookie_jar as $name => $value) {
            if (empty($cookies))
                $cookies = "$name=$value";
            else
                $cookies .= "; $name=$value";
        }
        if ($cookies != null)
            curl_setopt($this->ch,CURLOPT_COOKIE,$cookies);
        curl_setopt($this->ch,CURLOPT_MAXREDIRS,10);
        curl_setopt($this->ch,CURLOPT_HEADER,0);
        curl_setopt($this->ch,CURLOPT_RETURNTRANSFER,1);
        curl_setopt($this->ch,CURLOPT_TIMEOUT,30);
        curl_setopt($this->ch,CURLOPT_CONNECTTIMEOUT,10);
        curl_setopt($this->ch,CURLOPT_HTTPGET,1);
        $data = curl_exec($this->ch);
        return $data;
    }

    function setHTTPcreds($uname,$pwd) {
        curl_setopt($this->ch, CURLOPT_HTTPAUTH, CURLAUTH_BASIC);
        curl_setopt($this->ch, CURLOPT_USERPWD, $uname.":".$pwd);
    }

    function __destruct () {
        curl_close($this->ch);
        @unlink('/tmp/huggle.cookies.'.$this->uid.'.dat');
    }
	
}//end of http class

?>